home *** CD-ROM | disk | FTP | other *** search
- /* Developed with Turbo (Borland) C++ for DOS, version 3.0.
- Will compile only as COMPACT, LARGE, or HUGE, and the model
- must match the .MODEL directive in the ASM files. */
-
- // #define registered_version
-
- /* ----------------------------------------------------------------------
-
- PCX.CPP
- Version 1.1
-
- Copyright (c) 1994
- by Peter Donnelly
- Skookum Software
- 1301 Ryan Street
- Victoria BC Canada V8T 4Y8
-
- ╒══════════════════════════════════════════════════════════════════════╕
- │ Permission is granted for the non-commercial distribution and │
- │ private use of this source code. This is shareware; if you use all │
- │ or portions of it in programs you distribute, or make any other │
- │ public use of it, you are expected to pay a modest registration │
- │ fee. Registered users will receive the latest version of the code, │
- │ including support for 256-color Super-VGA modes. Please see the │
- │ READ.ME file for details. │
- ╘══════════════════════════════════════════════════════════════════════╛
- */
- #include "pcx.h"
- #include <stdio.h>
- #include <conio.h>
- #include <io.h>
- #include <string.h>
- #include <dos.h>
- #include <alloc.h>
-
- #define VIDEO 0x10
- #ifdef __cplusplus // don't mangle function names
- extern "C" {
- #endif
- void Decode16(void);
- void Decode256(void);
- void DecodeSVGA256(void);
- #ifdef __cplusplus
- }
- #endif
-
- // Assembler variables
-
- int file_error;
- unsigned int ColumnCount;
- unsigned int Plane;
- unsigned int BytesPerLine;
- unsigned char RepeatCount;
- unsigned int DataLength;
- int LineEnd, ScreenWidth;
- int Margin;
- unsigned int WindowEnd, WindowStep, WindowPos;
- int BytesPerScanLine;
- int StartCol, XMax;
- int far *LineBuf;
- unsigned int LineBufSeg, LineBufOffs, LineBufIndex;
- int far *Scratch; // needs to be normalized
- unsigned int VideoSeg, VideoOffs;
- char WriteWindow;
- unsigned int buffer_size;
- const num_modes = 11;
- union REGS inregs, outregs;
- struct SREGS segregs;
- FILE *pcx_file;
- pathstr pcx_filename;
- char *error_str;
- int pic_width;
- int our_modes[num_modes] = {0x0D, 0x0E, 0x10, 0x12, 0x13, 0x100, 0x101,
- 0x102, 0x103, 0x105, 0x107};
-
-
- /* ------------------------ Structures ---------------------------- */
-
- rgb_struct rgb_pal[15];
- pcx_header_struct pcx_header;
- VESA_info_struct VESA_info;
- mode_info_struct mode_info;
-
- typedef rgb_struct cregisters[256];
- cregisters rgb256;
-
- /* ---------------------- Video mode functions ------------------------- */
-
- void video_off(BOOLEAN vid_status)
- // Hides the image by turning off video refresh. See Ferraro p. 468.
- {
- inregs.h.ah = 0x12;
- inregs.h.bl = 0x36;
- inregs.h.al = vid_status;
- int86(VIDEO, &inregs, &outregs);
- }
-
-
- int detect_VESA(VESA_info_struct *VESA_rec) // returns !0 if VESA BIOS present
- {
- inregs.x.ax = 0x4F00;
- segregs.es = FP_SEG(VESA_rec);
- inregs.x.di = FP_OFF(VESA_rec);
- int86x(VIDEO, &inregs, &outregs, &segregs);
- if (outregs.h.ah) return 0;
- return(!strncmp(VESA_rec->signature, "VESA", 4));
- }
-
-
- int detect_VGA(void) // see Ferraro p. 887
- {
- inregs.h.ah = 0x1A;
- inregs.h.al = 0;
- int86(VIDEO, &inregs, &outregs);
- return(outregs.h.al != 0x1A);
- }
-
-
- int hardware_supports(unsigned int mode) // returns 1 if mode supported
- {
- unsigned int far *modeptr;
-
- if (mode >= 0x100) {
- if (detect_VESA(&VESA_info)) { // fills VESA_info structure
- modeptr = VESA_info.mode_list_ptr;
- do {
- if (*modeptr++ == mode) return 1;
- } while (*modeptr != 0xFFFF);
- }
- return 0; // didn't find match, or no VESA
- }
- return 1; // not VESA mode; assume supported
- }
-
- int we_support(unsigned int mode) // returns 1 if mode supported
- {
- int x;
- int in_there = 0;
-
- for (x = 0; x < num_modes; x++) {
- if (mode == our_modes[x]) in_there = 1; }
- return in_there;
- }
-
- int fits(pcx_header_struct header)
- {
- return((header.xmax < header.hres) && (header.ymax < header.vres));
- }
-
- void try_it(unsigned int mode, unsigned int *m)
- {
- if ((hardware_supports(mode)) && (we_support(mode)))
- *m = mode;
- return;
- }
-
- unsigned int best_mode(pcx_header_struct header)
- /* Finds the lowest resolution at which the picture will fit on screen
- but not lower than the originating device. */
- {
- unsigned int m;
-
- if (header.num_planes == 1) {
- m = 0x13;
- if ((header.hres > 320) || (!fits(header))) try_it(0x101, &m);
- if ((header.hres > 640) || (!fits(header))) try_it(0x103, &m);
- if ((header.hres > 800) || (!fits(header))) try_it(0x105, &m);
- if ((header.hres > 1024) || (!fits(header))) try_it(0x107, &m);
- } else if (header.num_planes == 4) {
- m = (header.hres <= 320) ? 0x0D : 0x0E;
- if ((header.vres > 200) || (!fits(header))) try_it(0x10, &m);
- if ((header.vres > 350) || (!fits(header))) try_it(0x12, &m);
- if ((header.vres > 480) || (!fits(header))) try_it(0x102, &m);
- } else {
- file_error = 5;
- m = 0xFFFF;
- }
- return m;
- }
-
- void get_mode_info(unsigned int mode, mode_info_struct *m)
- // puts info on any VESA mode into the structure.
- {
- inregs.x.ax = 0x4F01; // VESA function
- inregs.x.cx = mode;
- segregs.es = FP_SEG(m);
- inregs.x.di = FP_OFF(m);
- int86x(VIDEO, &inregs, &outregs, &segregs);
- /* Early versions of VESA BIOS extensions do not return values in the
- xres and yres fields. We need to know yres for centering images. */
- switch(mode) {
- case 0x100: m->yres = 400; break;
- case 0x101: m->yres = 480; break;
- case 0x102: m->yres = 600; break;
- case 0x103: m->yres = 600; break;
- case 0x105: m->yres = 768; break;
- case 0x107: m->yres = 1024;
- }
- return;
- }
-
- int get_mode(void)
- {
- int curr_mode;
-
- if (detect_VESA(&VESA_info)) {
- inregs.x.ax = 0x4F03;
- int86(VIDEO, &inregs, &outregs);
- curr_mode = outregs.x.bx; // may be inaccurate if not SVGA
- curr_mode &= 0x3FFF; // - see Wilton p. 448
- if (hardware_supports(curr_mode) && (curr_mode >= 0x100))
- return curr_mode;
- }
- inregs.h.ah = 0x0F; // return VGA mode
- int86(VIDEO, &inregs, &outregs);
- return outregs.h.al;
- }
-
- void set_mode(int mode, int options)
- {
- if (mode >= 0x100) {
- if (options & save_mem) mode |= 0x8000;
- // set bit 15 to preserve video mem.
- inregs.x.ax = 0x4F02;
- inregs.x.bx = mode;
- } else {
- if (options & save_mem) mode |= 0x80;
- // set bit 7 to preserve video mem.
- inregs.x.ax = mode; // only use low byte
- inregs.h.ah = 0; // function no.
- }
- int86(VIDEO, &inregs, &outregs);
- return;
- }
-
- void put_window(int step, char window)
- {
- inregs.x.ax = 0x4f05; // VESA window function
- inregs.h.bh = 0; // move-window subfunction
- inregs.h.bl = window; // window A NEED TO CHECK WRITABILITY!!
- inregs.x.dx = step; // window position, in granules
- int86(VIDEO, &inregs, &outregs);
- }
-
- /* ------------------------ Palette functions ------------------------- */
-
- void set_color_registers(cregisters rgb)
- {
- // Replaces the BGI setrgbpalette function
-
- inregs.h.ah = 0x10; // function
- inregs.h.al = 0x12; // subfunction
- segregs.es = FP_SEG(rgb); // list of colors to put in
- inregs.x.dx = FP_OFF(rgb);
- inregs.x.bx = 0; // first register to change
- inregs.x.cx = 0x100; // how many regs to change
- int86x(VIDEO, &inregs, &outregs, &segregs);
- return;
- }
-
- void set_palette(unsigned char *palette)
- {
- // Replaces the BGI setallpalette function
-
- inregs.h.ah = 0x10; // function
- inregs.h.al = 0x02; // subfunction
- segregs.es = FP_SEG(palette); // list of 17 values to put in
- inregs.x.dx = FP_OFF(palette);
- int86x(VIDEO, &inregs, &outregs, &segregs);
- return;
- }
-
- long get_256_palette(FILE *the_file, cregisters rgb256)
-
- /* File must be open. Returns palette offset if present, 0 if not.
-
- The last 769 bytes of the file are palette information, starting with
- a one-byte identifier. Each group of three bytes represents the RGB
- values of one of the color registers. We take the 6 most significant
- bits to bring the values within the range 0-63 expected by the registers. */
- {
- unsigned char palette_flag;
- long palette_start;
- int x;
-
- palette_start = filelength(fileno(the_file)) - 769;
- fseek(the_file, palette_start, SEEK_SET);
- palette_flag = getc(the_file);
- if ((palette_flag != 12) || (pcx_header.version < 5)
- || (fread(rgb256, 3, 256, the_file) < 256)) {
- file_error = 2;
- return 0;
- }
- for (x = 0; x < 256; x++) {
- rgb256[x].r >>= 2;
- rgb256[x].g >>= 2;
- rgb256[x].b >>= 2;
- }
- return palette_start;
- }
-
- /* ---------------------- Miscellaneous functions ---------------------- */
-
- FILE *open_file(pathstr pic_file_name, char *file_mode,
- pcx_header_struct *header)
- {
- FILE *f;
-
- f = fopen(pic_file_name, file_mode);
- if (f != NULL) fread(header, 1, 128, f);
- return f;
- }
-
- char *report_error(int error)
- {
- switch(error) {
- case 1: return "Could not open file.\n";
- case 2: return "No palette information in file.\n";
- case 3: return "Picture is too wide for requested video mode.\n";
- case 4: return "Number of colors in file does not match selected mode.\n";
- case 5: return "Unsupported picture format.\n";
- default: return "Undefined error.\n";
- }
- }
-
- void get_margin(int screenwd, int *margin, int *line_end,
- pcx_header_struct header)
- {
- // Calculates how many pixels have to be skipped when advancing to
- // the next line, so files of less than screen width can be displayed
- // and centered.
-
- *line_end = header.bytes_per_line;
- *margin = screenwd - *line_end;
- if (*margin < 0) file_error = 3; // too wide
- return;
- }
-
- unsigned int set_buffer_size(void)
- {
- unsigned int buf;
-
- buf = 64512;
- if (buf > coreleft())
- buf = coreleft() - (coreleft() % 1024);
- // fread is faster if buffer_size is a round number?
- return buf;
- }
-
- long get_first_pix(pcx_header_struct header, int optns,
- int screenwid, int screenht)
-
- /* The image is centered if the Options call for it. Otherwise it is offset
- on the screen according to the values of XMin and YMin in the file header.
- These are usually zero. This function returns the offset in bytes from
- the start of the video buffer to where the first pixel will be written. */
- {
-
- long first_pix = 0;
- int picwid, picht;
-
- picwid = header.xmax - header.xmin + 1;
- if (header.bits_per_plane == 1) picwid /= 8;
- picht = header.ymax - header.ymin;
- if (picht < screenht) {
- if (!(optns & vcenter))
- first_pix += (long)header.ymin * screenwid;
- else first_pix += (long)(screenht-1-picht) / 2 * screenwid;
- };
- if (picwid < screenwid) {
- if (!(optns & hcenter)) first_pix += header.xmin;
- else first_pix += (screenwid - picwid) / 2;
- };
- return first_pix;
- }
-
-
- /* ---------------- Main procedure for 16-color modes ------------------ */
-
- int read_16(FILE *pic_file, unsigned int mode, unsigned int options)
- {
- // don't call directly; needs pcx_header initialized by read_it()
- typedef unsigned char palette_bytes[3];
-
- unsigned char entry, gun, pcx_code;
- unsigned char pal_regs[17];
- unsigned int screen_height;
-
- if (pcx_header.num_planes != 4) return 4;
- if (mode >= 0x100) {
- get_mode_info(mode, &mode_info);
- ScreenWidth = mode_info.bytes_per_line;
- screen_height = mode_info.yres;
- } else
- switch(mode) {
- case 0x0D: ScreenWidth = 40; screen_height = 200; break;
- case 0x0E: ScreenWidth = 80; screen_height = 200; break;
- case 0x10: ScreenWidth = 80; screen_height = 350; break;
- case 0x12: ScreenWidth = 80; screen_height = 480; break;
- }
- get_margin(ScreenWidth, &Margin, &LineEnd, pcx_header);
- if (file_error) return file_error;
- VideoOffs = get_first_pix(pcx_header, options, ScreenWidth, screen_height);
- VideoSeg = 0xA000; // Segment of video memory
- outportb(0x3C4, 2); // Index to map mask register }
- Plane = 1; // Initialize plane }
- outportb(0x3C5, Plane); // Set sequencer to mask out other planes }
-
- // --- Decipher 16-color palette
-
- /* The palette information is stored in bytes 16-63 of the header. Each of
- the 16 palette slots is allotted 3 bytes - one for each primary color.
- Any of these bytes can have a value of 0-255. However, the VGA is
- capable only of 6-bit RGB values (making for 64x64x64 = 256K possible
- colors), so we take only the 6 most significant bits from each PCX
- color value.
-
- In 16-color modes, the VGA uses the 16 CGA/EGA palette registers.
- However, the actual color values (18 bits per slot) won't fit here,
- so the palette registers are used as pointers to 16 of the 256 color
- registers, which hold the RGB values.
-
- What we have to do is extract the RGB values from the PCX header, put
- them in the first 16 color registers, then set the palette to point to
- those registers. */
-
- for (entry = 0; entry < 16; entry++) {
- for (gun = 0; gun < 3; gun++) {
- pcx_code = ((palette_bytes &)pcx_header.palette[entry])[gun];
- switch (gun) {
- case 0: rgb_pal[entry].r = pcx_code >> 2; break;
- case 1: rgb_pal[entry].g = pcx_code >> 2; break;
- case 2: rgb_pal[entry].b = pcx_code >> 2;
- }
- }
- pal_regs[entry] = entry;
- }
- pal_regs[16] = 0; // overscan color
- set_color_registers(rgb_pal); // RGB values into registers 0-15
- set_palette(pal_regs); // point to registers 0-15
-
- // --- Read and decode the image data ---
-
- BytesPerLine = pcx_header.bytes_per_line;
- RepeatCount = 0; // Initialize assembler vars.
- ColumnCount = 0;
- buffer_size = set_buffer_size();
- Scratch = (int far*)malloc(buffer_size);
- fseek(pic_file, 128, SEEK_SET);
- do {
- DataLength = fread(Scratch, 1, buffer_size, pic_file);
- Decode16();
- } while (!feof(pic_file));
- free(Scratch);
- outportb(0x3C5, 0xF); // Reset mask map
- return 0;
- }
-
- /* -------------------------- VGA 256-color modes ---------------------- */
-
- int read_256(FILE *pic_file, unsigned int mode, unsigned int options)
- {
- // don't call directly; needs pcx_header initialized by read_it()
- long palette_start, total_read;
-
- if (pcx_header.num_planes != 1) return 4;
- palette_start = get_256_palette(pic_file, rgb256);
- if (!palette_start) return 2;
- ScreenWidth = 320;
- get_margin(ScreenWidth, &Margin, &LineEnd, pcx_header);
- if (file_error) return file_error;
- fseek(pic_file, 128, SEEK_SET);
- total_read = 128;
- RepeatCount = 0;
- set_color_registers(rgb256);
- VideoOffs = get_first_pix(pcx_header, options, ScreenWidth, 200);
- VideoSeg = 0xA000;
- buffer_size = set_buffer_size();
- Scratch = (int far*)malloc(buffer_size);
- do {
- DataLength = fread(Scratch, 1, buffer_size, pic_file);
- total_read += DataLength;
- if (total_read > palette_start)
- DataLength -= (total_read - palette_start);
- Decode256();
- } while ((!feof(pic_file)) && (total_read < palette_start));
- free(Scratch);
- if (mode); // to stop compiler warning
- return 0;
- }
-
- /* ---------------------- SVGA 256-color files ------------------------- */
-
- #ifdef registered_version
- #include "svga256.h"
- #else
-
- int read_SVGA256(FILE *pic_file, unsigned int mode, unsigned int options)
- {
- set_mode(3, no_options);
- printf("Support for this video mode is available only to registered\n"
- "users of PCX.CPP. Please see READ.ME for details.\n");
- return 0;
- }
- #endif
-
- /* ---------------------------- read_it() ------------------------------ */
-
- int read_it(pathstr pic_file_name, int mode, int options)
- { // returns file_error
- FILE *pcx_file;
-
- file_error = 0;
- pcx_file = open_file(pic_file_name, "rb", &pcx_header);
- if (pcx_file == NULL) return 1;
- if ((pcx_header.bits_per_plane < 8) && (pcx_header.num_planes == 1)) {
- fclose(pcx_file);
- return 5;
- }
- if (mode == auto_set) mode = best_mode(pcx_header);
- set_mode(mode, options);
- if (options & blackout) video_off(true);
- switch(mode) {
- case 0x0D:
- case 0x0E:
- case 0x10:
- case 0x12:
- case 0x102: file_error = read_16(pcx_file, mode, options);
- break;
- case 0x13: file_error = read_256(pcx_file, mode, options);
- break;
- case 0x100:
- case 0x101:
- case 0x103:
- case 0x105:
- case 0x107: file_error = read_SVGA256(pcx_file, mode, options);
- break;
- }
- if (options & blackout) video_off(false);
- fclose(pcx_file);
- return file_error;
- }
-